package main

import (
	"http"
	"io"
	"flag"
	"os"
	"fmt"
	"strings"
//	"time"
)

/* 	
	This is the main server program. A new thread is instantiated 
	for each request and handled in the keyServer function
*/

// The file that specifies the list of servers
var serverFile string;

// Current server's ID. To be loaded at start time.
// Either using the position in the servers list, or directly from the command line.
var serverID int;

// Initializes / binds all the flags
func initFlags() {
	// Flag name to input the file with servers - serverFile
     	flag.StringVar(&serverFile, "serverFile", "servers.txt", "The file that specifies the list of servers")
	flag.IntVar(&serverID, "id", 1, "This specifies the ID of the current server")
}

// Extracts key value pairs from the input request
// '/asdasda[a]=[b]sdads/sds' will produce as output a and b
func extractkeyvalue(originalString string) (key string, value string) {
	
	n := len(originalString)
	i := n-1

	//Start and end of the [key]=[value] part of the string
	start := 0
	end := n

	//Finding start point
	for j,v := range originalString {
		if v == '[' {
			start = j
			break
		}
	} 

	//Finding end point
	for ; i > 0 ; i-- {
		if originalString[i] == ']' {
			end = i
			break
		}
	}

	//Where equal to occurs in the string. -1 denotes it does not occur
	equalto := -1

	//Finding equal to
	for i = start + 1; i < end; i++ {
		if originalString[i]=='=' && i-1 > 0 && originalString[i-1]==']' && i+1 < end && originalString[i+1] =='[' {
			equalto = i
			break
		}
	}

	// Extracting key value in seperate strings
	if equalto != -1 {
		for k:= start+1 ; k < equalto-1; k++ {
			key+=string(originalString[k]);
		}
		for k:= equalto+2; k < end; k++ {
			value+=string(originalString[k]);
		}
		return key,value
	}

	// Value doesn't exist, so just return key
	for k:=start+1; k<end; k++ {
		key+=string(originalString[k]);
	}

	return key,value
}

// This is the own thread for each request - Does read or write depending
// on the request
func keyServer(c *http.Conn, req *http.Request) {
	key,value := extractkeyvalue(req.RawURL);
	switch {
		case len(value)!=0 :
			result := "["
			result+=key
			result+="]="
			rv :=handleWrite(key,value)
			if rv!="NIL" {
				 result+="["
				 result+=rv
				 result+="]"
			} else {
				result+=rv
			}
			io.WriteString(c,result);
		case len(value)==0 :
			result := "["
                        result+=key
                        result+="]="
			rv := handleRead(key)
			if rv!="NIL" {
				 result+="["
				 result+=rv
				 result+="]"
			} else {
				result+=rv
			}
			io.WriteString(c,result);
	}	
}

func parseServerFile(filename string) []string {
     file, err := os.Open(filename, os.O_RDWR, 777)
     if file == nil {
     	fmt.Printf("Error opening file of servers. Error : %s\n", err.String())
	os.Exit(1);
     }
     
     data := ""
     var buf [100] byte
     for ;; { 
          n, err := file.Read(&buf)

	  if n == 0 || err != nil { break }
	  data += string(buf[0:n])
     }

     servers := strings.Split(data, "\n", 0)
     return servers
}

// The Landing pad for the whole system. One thread for each request
func main() {
     	// Initialize parse command line flags
	initFlags();
	flag.Parse();

	// Parse the list of servers to hand it to partition manager
	servers := parseServerFile(serverFile);

	//Parsing server.txt
	parseIP(servers)

	//Initialize hash tables, one for each parition
	initHashTables(n_servers);

	//Initialize logs, one for each partition
	initLog(n_servers);

	//Setting up exporters and importers
	estatus := setupExporters()
	if estatus != true {
		panic("Error setting up exporters!")
	}
	fmt.Println("Setup of exporters done")
	setupImporters();

	// Initialze the partition manager
	initPartitionManager(servers);

	http.Handle("/", http.HandlerFunc(keyServer));
	
	err := http.ListenAndServe(":"+my_port, nil);
	if err != nil {
		panic("ListenAndServe: ", err.String())
	}
	
}
